package vip.liux.backend.infrastructure.jpa.pay;

import jakarta.persistence.criteria.Predicate;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.transaction.annotation.Transactional;
import vip.liux.contracts.models.pay.PayNotifyTask;
import vip.liux.contracts.repositories.pay.PayNotifyTaskRepository;
import vip.liux.contracts.shared.enums.pay.PayNotifyStatus;
import vip.liux.contracts.shared.enums.pay.PayNotifyType;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;

public interface PayNotifyTaskJpaRepository extends JpaRepository<PayNotifyTask, Long>, PayNotifyTaskRepository, JpaSpecificationExecutor<PayNotifyTask> {

    @Override
    default List<PayNotifyTask> selectListByNotify() {
        return findAll((root, query, criteriaBuilder) -> {
            List<Predicate> predicates = new ArrayList<>();
            predicates.add(root.get("status").in(List.of(PayNotifyStatus.WAITING.getValue(), PayNotifyStatus.REQUEST_SUCCESS.getValue(), PayNotifyStatus.REQUEST_FAILURE.getValue())));
            predicates.add(criteriaBuilder.lessThanOrEqualTo(root.get("nextNotifyTime"), LocalDateTime.now()));
            return query.where(predicates.toArray(new Predicate[0])).getRestriction();
        });
    }

    @Transactional
    @Modifying
    @Query("update PayNotifyTask p set p.lastExecuteTime = ?1, p.notifyTimes = ?2, p.status = ?3 where p.id = ?4")
    void updateLastExecuteTimeAndNotifyTimesAndStatusById(LocalDateTime lastExecuteTime, Integer notifyTimes, PayNotifyStatus status, Long id);

    @Override
    default Page<PayNotifyTask> findPage(
            Long appId, PayNotifyType type, String dataNo, PayNotifyStatus status, String merchantOrderId, String merchantRefundId,
            String merchantTransferId, LocalDateTime start, LocalDateTime end, Pageable page) {
        return findAll((root, query, criteriaBuilder) -> {
            List<Predicate> predicates = new ArrayList<>();
            if (appId != null) {
                predicates.add(criteriaBuilder.equal(root.get("appId"), appId));
            }
            if (type != null) {
                predicates.add(criteriaBuilder.equal(root.get("type"), type));
            }
            if (dataNo != null) {
                predicates.add(criteriaBuilder.equal(root.get("dataNo"), dataNo));
            }
            if (status != null) {
                predicates.add(criteriaBuilder.equal(root.get("status"), status));
            }
            if (merchantOrderId != null && !merchantOrderId.isEmpty()) {
                predicates.add(criteriaBuilder.equal(root.get("merchantOrderId"), merchantOrderId));
            }
            if (merchantRefundId != null && !merchantRefundId.isEmpty()) {
                predicates.add(criteriaBuilder.equal(root.get("merchantRefundId"), merchantRefundId));
            }
            if (merchantTransferId != null && !merchantTransferId.isEmpty()) {
                predicates.add(criteriaBuilder.equal(root.get("merchantTransferId"), merchantTransferId));
            }
            if (start != null) {
                predicates.add(criteriaBuilder.greaterThanOrEqualTo(root.get("creationTime"), start));
            }
            if (end != null) {
                predicates.add(criteriaBuilder.lessThanOrEqualTo(root.get("creationTime"), end));
            }
            return query.where(predicates.toArray(new Predicate[0])).getRestriction();
        }, page);
    }

}